package ru.space_game.world;

import java.util.ArrayList;
import java.util.List;

import ru.space_game.core.Game;
import ru.space_game.entities.AlienEntity;
import ru.space_game.entities.Entity;
import ru.space_game.entities.ShipEntity;
import ru.space_game.resource.Sounds;

public class SpaceWorld {

	private List<Entity> entities = new ArrayList<Entity>();

	private List<Entity> visibleEntities = new ArrayList<Entity>();

	/** The list of entities that need to be removed from the game this loop */
	private List<Entity> removeList = new ArrayList<Entity>();

	private List<Entity> addList = new ArrayList<Entity>();

	private ShipEntity ship;

	private int alienCount;

	/**
	 * True if game logic needs to be applied this loop, normally as a result of
	 * a game event
	 */
	private boolean logicRequiredThisLoop;

	public void moveEntities(long delta) {
		// cycle round asking each entity to move itself
		if (!Game.getInstance().isWaitingForKeyPress()
				&& !Game.getInstance().getSoundManager().isPlayingSound()) {
			for (Entity entity : entities) {
				boolean wasVisible = entity.isVisible();
				entity.setVisible(ship);
				if (wasVisible && !entity.isVisible())
					visibleEntities.remove(entity);
				if (!wasVisible && entity.isVisible())
					visibleEntities.add(entity);

				entity.move(delta);
			}
		}
		entities.addAll(addList);
		addList.clear();
	}

	public void drawEntities() {
		// cycle round drawing all the visible entities we have in the game
		for (Entity entity : visibleEntities) {
			entity.draw();
		}
	}

	/**
	 * Initialize the starting state of the entities (ship and aliens). Each
	 * entity will be added to the overall list of entities in the game.
	 */
	public void initEntities() {
		// create the player ship and place it roughly in the center of the
		// screen
		ship = new ShipEntity(Game.getInstance(), "ship.gif",
				(Game.SCREEN_WIDTH / 2), (Game.SCREEN_HEIGHT / 2));
		entities.add(ship);

		// create a block of aliens (5 rows, by 12 aliens, spaced evenly)
		alienCount = 0;
		for (int i = 0; i < 20; i++) {
			Entity alien = new AlienEntity(Game.getInstance(),
					(int) (Math.random() * 2000), (int) (Math.random() * 2000));
			entities.add(alien);
			alienCount++;
		}

		visibleEntities = new ArrayList<Entity>();
		visibleEntities.add(ship);
	}

	public void removeEntities() {
		// remove any entity that has been marked for clear up
		entities.removeAll(removeList);
		visibleEntities.removeAll(removeList);
		removeList.clear();
	}

	/**
	 * Remove an entity from the game. The entity removed will no longer move or
	 * be drawn.
	 * 
	 * @param entity
	 *            The entity that should be removed
	 */
	public void removeEntity(Entity entity) {
		removeList.add(entity);
	}

	public void clearEntities() {
		entities.clear();
	}

	/**
	 * Notification that an alien has been killed
	 */
	public void notifyAlienKilled() {
		// reduce the alien count, if there are none left, the player has won!
		alienCount--;

		if (alienCount == 0) {
			Game.getInstance().notifyWin();
		}

		// if there are still some aliens left then they all need to get faster,
		// so
		// speed up all the existing aliens
		for (Entity entity : entities) {
			if (entity instanceof AlienEntity) {
				// speed up by fixed number %
				entity.setSpeed(entity.getSpeed() * 1.02f);
			}
		}

		Game.getInstance().getSoundManager().playEffect(Sounds.SOUND_HIT);
	}

	public ShipEntity getShip() {
		return ship;
	}

	public List<Entity> getEntities() {
		return entities;
	}

	public void processCollisions() {
		// brute force collisions, compare every entity against
		// every other entity. If any of them collide notify
		// both entities that the collision has occurred
		for (int p = 0; p < entities.size(); p++) {
			for (int s = p + 1; s < entities.size(); s++) {
				Entity me = entities.get(p);
				Entity him = entities.get(s);

				if (me.collidesWith(him)) {
					me.collidedWith(him);
					him.collidedWith(me);
				}
			}
		}
		removeEntities();
	}

	/**
	 * Notification from a game entity that the logic of the game should be run
	 * at the next opportunity (normally as a result of some game event)
	 */
	public void updateLogic() {
		logicRequiredThisLoop = true;
	}

	public void doLogic() {
		// if a game event has indicated that game logic should
		// be resolved, cycle round every entity requesting that
		// their personal logic should be considered.
		if (logicRequiredThisLoop) {
			for (Entity entity : entities) {
				entity.doLogic();
			}

			logicRequiredThisLoop = false;
		}
	}

	public List<Entity> getAddList() {
		return addList;
	}

}
